今天的內容,可能是這個project最精實的部份呀(汗)!
在利用script進行自動化的時候,一個最困難的地方,是如何在新建一個甚至一群Entity
的時候,給出適當的id
,讓我們後續的script工作能夠更順利進行。
今天我們總共要建立下面幾個function
:
get_ent_ids
_grab_id_ranges
_filter_id_ranges
_grab_filtered_id_range
_grab_id_range
get_fit_id_range
get_id
_grab_mix_id_range
get_fit_mix_id_range
get_mix_id
get_mat_prop_id
get_ent_ids
透過base.CollectEntities
來搜集整個ANSA database裡,某一個search_type
的全部Entity
,然候取出各Entity
的id
,回傳一個經過排序的list
。
# id_grabbers.py
def get_ent_ids(search_type, deck=None):
deck = deck or constants.LSDYNA
ents = base.CollectEntities(deck, None, search_type)
return sorted(ent._id for ent in ents)
_grab_id_ranges
接收一個id
的集合體叫ids
,可以看作是get_ent_ids
回傳的list
。
ids
是空的,我們就yield (1, 99999999, 99999999)
,代表當前可用的最小id
為1
,最大id
為99999999
,數量為99999999
。如果不是空的話。我們就將ids
丟進一個set
,預防有重覆id
出現的可能。0
與100000000
,方便我們做後續的計算。ids
丟回一個list
並進行排序。numpy
的diff
去得到ids
裡面,每兩個數字的差距,命名為id_diff
。id_diff
打一個迴圈,如果裡面有任何大於1
的情況,則代表其最少能夠插入一個id
,我們就yield (最小可用id,最大可用id,數量)
這個tuple
。簡單來說,_grab_id_ranges
會lazy的回傳每一個可用的id
區間。
# id_grabbers.py
import numpy as np
def _grab_id_ranges(ids):
max_n = 1_0000_0000
min_n = 0
if not ids:
yield (min_n+1, max_n-1, max_n - min_n-1)
else:
ids = set(ids)
ids.add(min_n)
ids.add(max_n)
ids = sorted(ids)
id_diff = np.diff(ids)
for id_, diff_ in zip(ids, id_diff):
if diff_ > 1:
yield (id_+1, id_+diff_-1, diff_-1)
_filter_id_ranges
幫助我們去找出可用的id
段裡,lazy地回傳可用數量大於req_n
的。
# id_grabbers.py
def _filter_id_ranges(req_n, id_ranges):
for start, end_, n in id_ranges:
if n >= req_n:
yield (start, end_)
_grab_filtered_id_range
透過_grab_id_ranges
得到可用的id ranges
,並透過_filter_id_ranges
回傳一個大於req_n
的id range
。
# id_grabbers.py
def _grab_filtered_id_range(req_n, ids):
id_ranges = _grab_id_ranges(ids)
filtered_id_range_iter = _filter_id_ranges(req_n, id_ranges)
return next(filtered_id_range_iter)
_grab_id_range
回傳某search_type
下,一個大於或等於req_n
的id_range
。
# id_grabbers.py
def _grab_id_range(req_n, search_type, deck=None):
ids = get_ent_ids(search_type, deck)
return _grab_filtered_id_range(req_n, ids)
get_fit_id_range
回傳某search_type
下,一個當下可用的fit id_range
。
# id_grabbers.py
def get_fit_id_range(req_n, search_type, deck=None):
start, _ = _grab_id_range(req_n, search_type, deck)
return start, start+req_n
get_id
回傳某search_type
下,當下可用的最小id
。
# id_grabbers.py
def get_id(search_type, deck=None):
start, _ = get_fit_id_range(1, search_type, deck)
return start
有時候,我們會想對齊
兩種不同Entity
的id
,例如一連串相同id
的material
及property
,此時_grab_mix_id_range
可以幫助我們。
_grab_mix_id_range
回傳同時考慮search_types
情況下,一個可用的id range
。
raise_for_not_put_in_a_container
檢查search_types
是否為iterable
。get_ent_ids
及set.union
去找出search_types
內各種type
用過的id
。_grab_filtered_id_range
,回傳適合的id range
。# id_grabbers.py
def _grab_mix_id_range(req_n, search_types, deck=None):
err_msg = f'{search_types=} might not be a suitable iterable.\n\
Try to put {search_types} in a list first.'
raise_for_not_put_in_a_container(
search_types, NotInContainerError, err_msg)
container = [set(get_ent_ids(type_, deck))
for type_ in search_types]
# Tricky
ids = sorted(reduce(set.union, container))
return _grab_filtered_id_range(req_n, ids)
get_fit_id_range
回傳某些search_types
下,一個當下可用的id_range
。
# id_grabbers.py
def get_fit_mix_id_range(req_n, search_types, deck=None):
start, *_ = _grab_mix_id_range(req_n, search_types, deck)
return start, start+req_n
get_id
回傳某些search_types
下,當下可用的最小id
。
# id_grabbers.py
def get_mix_id(search_types, deck=None):
start, _ = get_fit_mix_id_range(1, search_types, deck)
return start
get_mat_prop_id
回傳一個在同時考慮material
及property
情況下的最小可用id
。這可以幫助我們在建立Entity
時,有同樣的material
及property id
。
# id_grabbers.py
def get_mat_prop_id(deck=None):
search_types = [LSDYNAType.MATERIAL.value, LSDYNAType.PROPERTY.value]
return get_mix_id(search_types, deck)